package com.qunar.train.mock.logger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.qunar.train.mock.config.MockQconfig;
import com.qunar.train.mock.util.AegisLog;
import com.qunar.train.mock.util.ClassFieldTrick;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import mockit.Mock;
import mockit.MockUp;

public class MockLogger implements Logger {
    public static MockLogger instance = new MockLogger();

    public static ThreadLocal<MockLogger> threadLocal = new ThreadLocal<>();

    public static MockLogger getInstance() {
        if (threadLocal.get() == null) {
            threadLocal.set(MockLogger.instance);
        }
        return threadLocal.get();
    }

    final public String ROOT_LOGGER_NAME = "ROOT";

    //增加Map类型区分class存储，解决case批量并行运行问题
    private Map<String, Object> mapinfo = new HashMap<>();
    private Map<String, Object> maperror = new HashMap<>();
    private Map<String, Object> maptrace = new HashMap<>();
    private Map<String, Object> mapdebug = new HashMap<>();
    private Map<String, Object> mapwarn = new HashMap<>();
    private Map<String, Object> maplog = new HashMap<>();

    private String className;

    private String getNameReturnValue;
    private boolean isTraceEnabledReturnValue;
    private boolean isTraceEnabledReturnValuePMarker;
    private boolean isDebugEnabledReturnValue;
    private boolean isDebugEnabledReturnValuePMarker;
    private boolean isInfoEnabledReturnValue;
    private boolean isInfoEnabledReturnValuePMarker;
    private boolean isWarnEnabledReturnValue;
    private boolean isWarnEnabledReturnValuePMarker;
    private boolean isErrorEnabledReturnValue;
    private boolean isErrorEnabledReturnValuePMarker;

    public MockLogger() {
        getNameReturnValue = "";
        isTraceEnabledReturnValue = false;
        isTraceEnabledReturnValuePMarker = false;
        isDebugEnabledReturnValue = false;
        isDebugEnabledReturnValuePMarker = false;
        isInfoEnabledReturnValue = false;
        isInfoEnabledReturnValuePMarker = false;
        isWarnEnabledReturnValue = false;
        isWarnEnabledReturnValuePMarker = false;
        isErrorEnabledReturnValue = false;
        isErrorEnabledReturnValuePMarker = false;
    }

    public MockLogger(String className) {
        this.className = className;
        getNameReturnValue = "";
        isTraceEnabledReturnValue = false;
        isTraceEnabledReturnValuePMarker = false;
        isDebugEnabledReturnValue = false;
        isDebugEnabledReturnValuePMarker = false;
        isInfoEnabledReturnValue = false;
        isInfoEnabledReturnValuePMarker = false;
        isWarnEnabledReturnValue = false;
        isWarnEnabledReturnValuePMarker = false;
        isErrorEnabledReturnValue = false;
        isErrorEnabledReturnValuePMarker = false;
    }

    public static void mock() {
        //两种mock方法均可行，二选一
        //new LoggerMockUp();
        loggerMockUp();

//		StackTraceElement stackTraceElement[] = Thread.currentThread().getStackTrace();
//		for(StackTraceElement ste : stackTraceElement){
//			System.out.println(ste.getClassName());
//		}
        try {
            String clazzName = "";
            StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
            for (StackTraceElement s : stackTraceElements) {
                if (s.getClassName().endsWith("Test")) {
                    clazzName = s.getClassName();
                    break;
                }
            }
            if (clazzName.equals("")) {
                clazzName = Thread.currentThread().getStackTrace()[4].getClassName();
            }
            //String clazzName = Thread.currentThread().getStackTrace()[3].getClassName();
            Class superclazz = Class.forName(clazzName).getSuperclass();
            AegisLog.info("SuperClass: " + superclazz.getName());
            Field fieldList[] = superclazz.getDeclaredFields();
            for (Field field : fieldList) {
                //System.out.println(field.getName());
                if (field.getType() == Logger.class) {
                    //boolean isStatic = Modifier.isStatic(field.getModifiers());
                    //if(isStatic) {
                    ClassFieldTrick.trickFieldValue(superclazz, field.getName(), new MockLogger(superclazz.getName()));
                    //}
                    break;
                }
            }
        } catch (Exception e) {
            AegisLog.error(e);
        }

    }

    public static void loggerMockUp() {
        new MockUp<LoggerFactory>(LoggerFactory.class) {
            @Mock
            public Logger getLogger(Class clazz) {
                return new MockLogger(clazz.getName());
            }

            @Mock
            public Logger getLogger(String name) {
                return new MockLogger(name);
            }
        };

        //mock日志系统 @Slf4j
        MockQconfig.setProperty(ch.qos.logback.classic.Logger.class, "FQCN", MockLogger.class.getName());
    }

    @Deprecated
    public static class LoggerMockUp extends MockUp<LoggerFactory> {
        @Mock
        public static Logger getLogger(Class clazz) {
            return new MockLogger(clazz.getName());
        }

        @Mock
        public static Logger getLogger(String name) {
            return new MockLogger(name);
        }
    }

    public String getName() {
        return getInstance().getNameReturnValue;
    }

    public boolean isTraceEnabled() {
        return getInstance().isTraceEnabledReturnValue;
    }

    public void trace(String msg) {
        AegisLog.log(msg);
        setTrace(this.className, msg);
    }

    public void trace(String format, Object arg) {
        AegisLog.log(format + " : " + toJsonString(arg));
        setTrace(this.className, format + " : " + toJsonString(arg));
    }

    public void trace(String format, Object arg1, Object arg2) {
        AegisLog.log(format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setTrace(this.className, format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void trace(String format, Object[] argArray) {
        AegisLog.log(format + " : " + toJsonString(argArray));
        setTrace(this.className, format + " : " + toJsonString(argArray));
    }

    public void trace(String msg, Throwable t) {
        AegisLog.log(msg + " : " + t.getMessage());
        setTrace(this.className, msg + " : " + t.getMessage());
    }

    public boolean isTraceEnabled(Marker marker) {
        return getInstance().isTraceEnabledReturnValuePMarker;
    }

    public void trace(Marker marker, String msg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg);
        setTrace(this.className, marker.getName() + " : " + msg);
    }

    public void trace(Marker marker, String format, Object arg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg));
        setTrace(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg));
    }

    public void trace(Marker marker, String format, Object arg1, Object arg2) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setTrace(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void trace(Marker marker, String format, Object[] argArray) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(argArray));
        setTrace(this.className, marker.getName() + " : " + format + " : " + toJsonString(argArray));
    }

    public void trace(Marker marker, String msg, Throwable t) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg + " : " + t.getMessage());
        setTrace(this.className, marker.getName() + " : " + msg + " : " + t.getMessage());
    }

    public boolean isDebugEnabled() {
        return getInstance().isDebugEnabledReturnValue;
    }

    public void debug(String msg) {
        AegisLog.log(msg);
        setDebug(this.className, msg);
    }

    public void debug(String format, Object arg) {
        AegisLog.log(format + " : " + toJsonString(arg));
        setDebug(this.className, format + " : " + toJsonString(arg));
    }

    public void debug(String format, Object arg1, Object arg2) {
        AegisLog.log(format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setDebug(this.className, format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void debug(String format, Object[] argArray) {
        AegisLog.log(format + " : " + toJsonString(argArray));
        setDebug(this.className, format + " : " + toJsonString(argArray));
    }

    public void debug(String msg, Throwable t) {
        AegisLog.log(msg + " : " + t.getMessage());
        setDebug(this.className, msg + " : " + t.getMessage());
    }

    public boolean isDebugEnabled(Marker marker) {
        return getInstance().isDebugEnabledReturnValuePMarker;
    }

    public void debug(Marker marker, String msg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg);
        setDebug(this.className, marker.getName() + " : " + msg);
    }

    public void debug(Marker marker, String format, Object arg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg));
        setDebug(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg));
    }

    public void debug(Marker marker, String format, Object arg1, Object arg2) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setDebug(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void debug(Marker marker, String format, Object[] argArray) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(argArray));
        setDebug(this.className, marker.getName() + " : " + format + " : " + toJsonString(argArray));
    }

    public void debug(Marker marker, String msg, Throwable t) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg + " : " + t.getMessage());
        setDebug(this.className, marker.getName() + " : " + msg + " : " + t.getMessage());
    }

    public boolean isInfoEnabled() {
        return getInstance().isInfoEnabledReturnValue;
    }

    public void info(String msg) {
        AegisLog.log(msg);
        setInfo(this.className, msg);
    }

    public void info(String format, Object arg) {
        AegisLog.log(format + " : " + toJsonString(arg));
        setInfo(this.className, format + " : " + toJsonString(arg));
    }

    public void info(String format, Object arg1, Object arg2) {
        AegisLog.log(format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setInfo(this.className, format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void info(String format, Object[] argArray) {
        AegisLog.log(format + " : " + toJsonString(argArray));
        setInfo(this.className, format + " : " + toJsonString(argArray));
    }

    public void info(String msg, Throwable t) {
        AegisLog.log(msg + " : " + t.getMessage());
        setInfo(this.className, msg + " : " + t.getMessage());
    }

    public boolean isInfoEnabled(Marker marker) {
        return getInstance().isInfoEnabledReturnValuePMarker;
    }

    public void info(Marker marker, String msg) {
        AegisLog.log(marker.getName() + " : " + msg);
        setInfo(this.className, marker.getName() + " : " + msg);
    }

    public void info(Marker marker, String format, Object arg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg));
        setInfo(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg));
    }

    public void info(Marker marker, String format, Object arg1, Object arg2) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setInfo(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void info(Marker marker, String format, Object[] argArray) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(argArray));
        setInfo(this.className, marker.getName() + " : " + format + " : " + toJsonString(argArray));
    }

    public void info(Marker marker, String msg, Throwable t) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg + " : " + t.getMessage());
        setInfo(this.className, marker.getName() + " : " + msg + " : " + t.getMessage());
    }

    public boolean isWarnEnabled() {
        return getInstance().isWarnEnabledReturnValue;
    }

    public void warn(String msg) {
        AegisLog.log(msg);
        setWarn(this.className, msg);
    }

    public void warn(String format, Object arg) {
        AegisLog.log(format + " : " + toJsonString(arg));
        setWarn(this.className, format + " : " + toJsonString(arg));
    }

    public void warn(String format, Object[] argArray) {
        AegisLog.log(format + " : " + toJsonString(argArray));
        setWarn(this.className, format + " : " + toJsonString(argArray));
    }

    public void warn(String format, Object arg1, Object arg2) {
        AegisLog.log(format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setWarn(this.className, format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void warn(String msg, Throwable t) {
        AegisLog.log(msg + " : " + t.getMessage());
        setWarn(this.className, msg + " : " + t.getMessage());
    }

    public boolean isWarnEnabled(Marker marker) {
        return getInstance().isWarnEnabledReturnValuePMarker;
    }

    public void warn(Marker marker, String msg) {
        AegisLog.log(marker.getName() + " : " + msg);
        setWarn(this.className, marker.getName() + " : " + msg);
    }

    public void warn(Marker marker, String format, Object arg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg));
        setWarn(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg));
    }

    public void warn(Marker marker, String format, Object arg1, Object arg2) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setWarn(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void warn(Marker marker, String format, Object[] argArray) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(argArray));
        setWarn(this.className, marker.getName() + " : " + format + " : " + toJsonString(argArray));
    }

    public void warn(Marker marker, String msg, Throwable t) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg + " : " + t.getMessage());
        setWarn(this.className, marker.getName() + " : " + msg + " : " + t.getMessage());
    }

    public boolean isErrorEnabled() {
        return getInstance().isErrorEnabledReturnValue;
    }

    public void error(String msg) {
        AegisLog.log(msg);
        setError(this.className, msg);
    }

    public void error(String format, Object arg) {
        AegisLog.log(format + " : " + toJsonString(arg));
        setError(this.className, format + " : " + toJsonString(arg));
    }

    public void error(String format, Object arg1, Object arg2) {
        AegisLog.log(format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setError(this.className, format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void error(String format, Object[] argArray) {
        AegisLog.log(format + " : " + toJsonString(argArray));
        setError(this.className, format + " : " + toJsonString(argArray));
    }

    public void error(String msg, Throwable t) {
        AegisLog.log(msg + " : " + t.getMessage());
        setError(this.className, msg + " : " + t.getMessage());
    }

    public boolean isErrorEnabled(Marker marker) {
        return getInstance().isErrorEnabledReturnValuePMarker;
    }

    public void error(Marker marker, String msg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg);
        setError(this.className, marker.getName() + " : " + msg);
    }

    public void error(Marker marker, String format, Object arg) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg));
        setError(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg));
    }

    public void error(Marker marker, String format, Object arg1, Object arg2) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
        setError(this.className, marker.getName() + " : " + format + " : " + toJsonString(arg1) + '\t' + toJsonString(arg2));
    }

    public void error(Marker marker, String format, Object[] argArray) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + format + " : " + toJsonString(argArray));
        setError(this.className, marker.getName() + " : " + format + " : " + toJsonString(argArray));
    }

    public void error(Marker marker, String msg, Throwable t) {
        if (null == marker) return;
        AegisLog.log(marker.getName() + " : " + msg + " : " + t.getMessage());
        setError(this.className, marker.getName() + " : " + msg + " : " + t.getMessage());
    }


    public static void setGetNameReturnValue(String p) {
        getInstance().getNameReturnValue = p;
    }

    public static void setIsTraceEnabledReturnValue(boolean p) {
        getInstance().isTraceEnabledReturnValue = p;
    }

    public static void setIsTraceEnabledReturnValuePMarker(boolean p) {
        getInstance().isTraceEnabledReturnValuePMarker = p;
    }

    public static void setIsDebugEnabledReturnValue(boolean p) {
        getInstance().isDebugEnabledReturnValue = p;
    }

    public static void setIsDebugEnabledReturnValuePMarker(boolean p) {
        getInstance().isDebugEnabledReturnValuePMarker = p;
    }

    public static void setIsInfoEnabledReturnValue(boolean p) {
        getInstance().isInfoEnabledReturnValue = p;
    }

    public static void setIsInfoEnabledReturnValuePMarker(boolean p) {
        getInstance().isInfoEnabledReturnValuePMarker = p;
    }

    public static void setIsWarnEnabledReturnValue(boolean p) {
        getInstance().isWarnEnabledReturnValue = p;
    }

    public static void setIsWarnEnabledReturnValuePMarker(boolean p) {
        getInstance().isWarnEnabledReturnValuePMarker = p;
    }

    public static void setIsErrorEnabledReturnValue(boolean p) {
        getInstance().isErrorEnabledReturnValue = p;
    }

    public static void setIsErrorEnabledReturnValuePMarker(boolean p) {
        getInstance().isErrorEnabledReturnValuePMarker = p;
    }

    public static Object getInfo(Class clazz) {
        if (getInstance().mapinfo.containsKey(clazz.getName())) {
            return getInstance().mapinfo.get(clazz.getName());
        } else {
            return "";
        }
    }

    public static Object getInfo(String name) {
        if (getInstance().mapinfo.containsKey(name)) {
            return getInstance().mapinfo.get(name);
        } else {
            return "";
        }
    }

    public void setInfo(String className, Object info) {
        getInstance().mapinfo.put(className, info);
        getInstance().maplog.put(className, info);
    }

    public static Object getError(Class clazz) {
        if (getInstance().maperror.containsKey(clazz.getName())) {
            return getInstance().maperror.get(clazz.getName());
        } else {
            return "";
        }
    }

    public static Object getError(String name) {
        if (getInstance().maperror.containsKey(name)) {
            return getInstance().maperror.get(name);
        } else {
            return "";
        }
    }

    public void setError(String className, Object error) {
        getInstance().maperror.put(className, error);
        getInstance().maplog.put(className, error);
    }

    public static Object getTrace(Class clazz) {
        if (getInstance().maptrace.containsKey(clazz.getName())) {
            return getInstance().maptrace.get(clazz.getName());
        } else {
            return "";
        }
    }

    public static Object getTrace(String name) {
        if (getInstance().maptrace.containsKey(name)) {
            return getInstance().maptrace.get(name);
        } else {
            return "";
        }
    }

    public void setTrace(String className, Object trace) {
        getInstance().maptrace.put(className, trace);
        getInstance().maplog.put(className, trace);
    }

    public static Object getDebug(Class clazz) {
        if (getInstance().mapdebug.containsKey(clazz.getName())) {
            return getInstance().mapdebug.get(clazz.getName());
        } else {
            return "";
        }
    }

    public static Object getDebug(String name) {
        if (getInstance().mapdebug.containsKey(name)) {
            return getInstance().mapdebug.get(name);
        } else {
            return "";
        }
    }

    public void setDebug(String className, Object debug) {
        getInstance().mapdebug.put(className, debug);
        getInstance().maplog.put(className, debug);
    }

    public static Object getWarn(Class clazz) {
        if (getInstance().mapwarn.containsKey(clazz.getName())) {
            return getInstance().mapwarn.get(clazz.getName());
        } else {
            return "";
        }
    }

    public static Object getWarn(String name) {
        if (getInstance().mapwarn.containsKey(name)) {
            return getInstance().mapwarn.get(name);
        } else {
            return "";
        }
    }

    public void setWarn(String className, Object warn) {
        getInstance().mapwarn.put(className, warn);
        getInstance().maplog.put(className, warn);
    }

    public static Object getLog(Class clazz) {
        if (getInstance().maplog.containsKey(clazz.getName())) {
            return getInstance().maplog.get(clazz.getName());
        } else {
            return "";
        }
    }

    public static String toJsonString(Object obj) {
        String jsonString = null;
        try {
            jsonString = JSON.toJSONString(obj, SerializerFeature.IgnoreNonFieldGetter);
        } catch (Exception e) {
            return obj.toString();
        }
        return jsonString;
    }

    public static Object getLog(String name) {
        if (getInstance().maplog.containsKey(name)) {
            return getInstance().maplog.get(name);
        } else {
            return "";
        }
    }

    public static void clear() {
        getInstance().mapinfo = new HashMap<>();
        getInstance().maperror = new HashMap<>();
        getInstance().maptrace = new HashMap<>();
        getInstance().mapdebug = new HashMap<>();
        getInstance().mapwarn = new HashMap<>();
        getInstance().maplog = new HashMap<>();
    }

}
